package ph.com.gs3.formalistics.view.document;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;

import ph.com.gs3.formalistics.global.constants.FormContentType;
import ph.com.gs3.formalistics.global.constants.FormContentTypeGroupings;
import ph.com.gs3.formalistics.global.utilities.logging.FLLogger;
import ph.com.gs3.formalistics.model.vo.business.User;
import ph.com.gs3.formalistics.model.vo.business.document.DocumentHeaderData;
import ph.com.gs3.formalistics.model.vo.business.form.FormFieldData;
import ph.com.gs3.formalistics.model.vo.business.form.FormViewContentData;
import ph.com.gs3.formalistics.model.vo.formcontent.EmbeddedViewData;
import ph.com.gs3.formalistics.view.document.abstractfields.AbstractFieldView;
import ph.com.gs3.formalistics.view.document.abstractfields.AbstractFieldView.AbstractFieldChangeListener;
import ph.com.gs3.formalistics.view.document.fields.CheckboxGroupField;
import ph.com.gs3.formalistics.view.document.fields.DropdownField;
import ph.com.gs3.formalistics.view.document.fields.RadioButtonGroupField;
import ph.com.gs3.formalistics.view.document.fields.TextField;
import ph.com.gs3.formalistics.view.document.fields.TextField.TextFieldType;
import ph.com.gs3.formalistics.view.document.specialfields.AttachmentField;
import ph.com.gs3.formalistics.view.document.specialfields.BarcodeScannerField;
import ph.com.gs3.formalistics.view.document.specialfields.BarcodeScannerField.BarcodeScannerListener;
import ph.com.gs3.formalistics.view.document.specialfields.DateTimePickerField;
import ph.com.gs3.formalistics.view.document.specialfields.DateTimePickerField.DateTimePickerListener;
import ph.com.gs3.formalistics.view.document.specialfields.DateTimePickerField.PickerType;
import ph.com.gs3.formalistics.view.document.specialfields.DynamicImageField;
import ph.com.gs3.formalistics.view.document.specialfields.DynamicImageField.DynamicImageFieldActionListener;
import ph.com.gs3.formalistics.view.document.specialfields.PickListField;
import ph.com.gs3.formalistics.view.document.specialfields.PickListField.PickListFieldListener;
import ph.com.gs3.formalistics.view.document.views.EmbeddedView;
import ph.com.gs3.formalistics.view.document.views.EmbeddedView.EmbeddedViewEventsListener;
import android.app.Activity;
import android.content.Context;
import android.view.View;

public class DocumentDynamicViewContentsManager implements DocumentViewContentsManager {

	public static final String TAG = DocumentDynamicViewContentsManager.class.getSimpleName();

	private List<View> documentViewContents;
	private Context context;
	private User currentUser;

	// Field Listeners
	private PickListFieldListener pickListFieldListener;
	private DateTimePickerListener dateTimePickerListener;
	private BarcodeScannerListener barcodeScannerListener;
	private EmbeddedViewEventsListener embeddedViewEventsListener;
	private DynamicImageFieldActionListener dynamicImageFieldActionListener;

	public DocumentDynamicViewContentsManager(Context context, User currentUser) {
		documentViewContents = new ArrayList<>();

		this.context = context;
		this.currentUser = currentUser;
	}

	@Override
	public View getCreatedDocumentViewContentsContainer() {
		DefaultDocumentFields defaultDocumentFields = new DefaultDocumentFields(context, this);
		return defaultDocumentFields;
	}

	@Override
	public void createDocumentViewsFromData(List<FormViewContentData> formViewContentDataList,
	        JSONObject fieldValues, DocumentHeaderData documentHeaderData, User currentUser) {

		for (FormViewContentData formViewContentData : formViewContentDataList) {
			String formViewContentId = formViewContentData.getId();

			// Get and assign the value of the field
			if (fieldValues.has(formViewContentId)
			        && FormContentTypeGroupings.isField(formViewContentData.getType())) {
				try {
					String value = fieldValues.getString(formViewContentId);
					((FormFieldData) formViewContentData).setValue(value);
				} catch (JSONException e) {
					FLLogger.d(
					        TAG,
					        "Unable to read value of field " + formViewContentId + " "
					                + e.getMessage());
					e.printStackTrace();
				}
			}

			if (formViewContentData instanceof FormFieldData) {
				// Generate the properties of the field from the form
				((FormFieldData) formViewContentData).setEnabled(documentHeaderData
				        .getFieldsEnabled().contains(formViewContentId));
				((FormFieldData) formViewContentData).setHidden(documentHeaderData
				        .getFieldsHidden().contains(formViewContentId));
				((FormFieldData) formViewContentData).setRequired(documentHeaderData
				        .getFieldsRequired().contains(formViewContentId));
			}

			View fieldView = createFormContent(formViewContentData);

			if (fieldView != null) {
				documentViewContents.add(fieldView);
			} else {
				FLLogger.d(TAG, "Unable to create field for " + formViewContentData.getId()
				        + ", it's probably unsupported");
			}

			if (formViewContentData.getType() == FormContentType.EMBEDDED_VIEW) {

				// EmbeddedView embeddedView = (EmbeddedView) fieldView;
				// DynamicEvent event =
				// EmbeddedViewDataJSONParser.eventFromEmbeddedView(embeddedView);

			}
		}

		mapViewContentEventsAndDependencies();

	}

	public void notifyFieldsRequired(List<String> fieldsThatFailedValidation) {
		for (String fieldId : fieldsThatFailedValidation) {
			FLLogger.d(TAG, fieldId + " failed validation");
			
			AbstractFieldView field = findFieldView(fieldId);
			
			if (field != null) {
				field.showError(AbstractFieldView.PROMPT_FIELD_REQUIRED);
			}
			
		}
	}
	
	public void mapViewContentEventsAndDependencies() {

		for (View fieldView : documentViewContents) {

			if (fieldView instanceof EmbeddedView) {
				final EmbeddedView embeddedView = (EmbeddedView) fieldView;
				EmbeddedViewData embeddedViewData = embeddedView.getEmbeddedViewData();
				String changeTriggerFieldViewId = embeddedViewData
				        .getSearchCompareToThisDocumentFieldId();

				final AbstractFieldView changeTriggerFieldView = findFieldView(changeTriggerFieldViewId);

				if (changeTriggerFieldView != null) {
					changeTriggerFieldView.addOnChangeListener(new AbstractFieldChangeListener() {

						@Override
						public void onChange(String newValue) {
							FLLogger.d(TAG, changeTriggerFieldView.getFieldId() + ": " + newValue);
							embeddedViewEventsListener.onSearchForEmbeddedViewRequested(newValue,
							        embeddedView);
						}
					});
				} else {
					FLLogger.e(TAG, "Unable to bind embedded view event to "
					        + changeTriggerFieldViewId + ", field not found.");
				}

			}

		}

	}

	/**
	 * Initializes listeners using the activity, this may throw an illegal state exception
	 * if the required listeners are not implemented by the activity
	 * 
	 * @param activity
	 */
	public void setSpecialFieldsListener(Activity activity) {

		String exceptionMessage = "The activity " + activity.getClass().getSimpleName()
		        + " must implement ";

		try {
			this.pickListFieldListener = (PickListFieldListener) activity;
		} catch (ClassCastException e) {
			throw new IllegalStateException(exceptionMessage + " PickListFieldListener");
		}

		try {
			this.dateTimePickerListener = (DateTimePickerListener) activity;
		} catch (ClassCastException e) {
			throw new IllegalStateException(exceptionMessage + " DateTimePickerListener");
		}

		try {
			this.barcodeScannerListener = (BarcodeScannerListener) activity;
		} catch (ClassCastException e) {
			throw new IllegalStateException(exceptionMessage + " BarcodeScannerListener");
		}

		try {
			this.embeddedViewEventsListener = (EmbeddedViewEventsListener) activity;
		} catch (ClassCastException e) {
			throw new IllegalStateException(exceptionMessage + " EmbeddedViewEventsListener");
		}

		try {
			this.dynamicImageFieldActionListener = (DynamicImageFieldActionListener) activity;
		} catch (ClassCastException e) {
			throw new IllegalStateException(exceptionMessage + " EmbeddedViewEventsListener");
		}
	}

	// =============================================================================
	// {{ Events

	@Override
	public void onLoad() {
		// TODO Add the dynamic events for creation here

	}

	// }}

	// =============================================================================
	// {{ Public Functional Methods

	/**
	 * Searches for the field view with the specified id. Note that this will not search
	 * for views (ie embedded views).
	 * 
	 * @param fieldId
	 * @return The field searched if it is found, otherwise, returns null.
	 */
	public AbstractFieldView findFieldView(String fieldId) {
		for (View viewContent : documentViewContents) {
			if (viewContent instanceof AbstractFieldView) {
				if (((AbstractFieldView) viewContent).getFieldId().equals(fieldId)) {
					return (AbstractFieldView) viewContent;
				}
			}
		}

		return null;
	}

	public void setFieldValue(String fieldId, String fieldValue) {
		AbstractFieldView field = findFieldView(fieldId);

		if (field != null) {
			field.setValue(fieldValue);
		} else {
			FLLogger.e(TAG, "Failed upding field " + fieldId + ", field not found.");
		}

	}

	public JSONObject getFieldValues() {

		JSONObject fieldValues = new JSONObject();

		for (View viewContent : documentViewContents) {
			if (viewContent instanceof AbstractFieldView) {
				AbstractFieldView fieldView = (AbstractFieldView) viewContent;
				try {
					fieldValues.put(fieldView.getFieldId(), fieldView.getValue());
				} catch (JSONException e) {
					FLLogger.e(TAG, "Failed to insert field value of " + fieldView.getFieldId()
					        + ", cause: " + e.getMessage());
				}
			}
		}

		return fieldValues;

	}

	// }}

	// =============================================================================
	// {{ Private Functional Methods

	private View createFormContent(FormViewContentData formContentData) {

		View fieldView = null;

		if (FormContentTypeGroupings.isField(formContentData.getType())) {
			FormFieldData formField = (FormFieldData) formContentData;

			switch (formContentData.getType()) {
				case TEXT_FIELD:
					fieldView = new TextField(context, formField);
					break;
				case TEXT_AREA:
					fieldView = new TextField(context, formField, TextFieldType.MULTILINE);
					break;
				case CHECK_BOX_GROUP:
				case SELECT_MANY:
					fieldView = new CheckboxGroupField(context, formField);
					break;
				case RADIO_BUTTON_GROUP:
					fieldView = new RadioButtonGroupField(context, formField);
					break;
				case DROPDOWN:
					fieldView = new DropdownField(context, formField);
					break;
				case PICK_LIST:

					// Code level validation
					if (pickListFieldListener == null) {
						throw new IllegalStateException(
						        "Field builder cannot create picklists without a picklist listener");
					}

					PickListField plField = new PickListField(context, formField);
					plField.setListener(pickListFieldListener);
					fieldView = plField;
					break;
				case DATE_PICKER:

					// Code level validation
					if (dateTimePickerListener == null) {
						throw new IllegalStateException(
						        "Field builder cannot create date pickers without a date time picker listener");
					}

					DateTimePickerField datePickerField = new DateTimePickerField(context,
					        formField, PickerType.DATE);
					datePickerField.setListener(dateTimePickerListener);
					fieldView = datePickerField;
					break;
				case DATE_TIME_PICKER:
					// Code level validation
					if (dateTimePickerListener == null) {
						throw new IllegalStateException(
						        "Field builder cannot create date pickers without a date time picker listener");
					}

					DateTimePickerField dateTimePickerField = new DateTimePickerField(context,
					        formField, PickerType.DATETIME);
					dateTimePickerField.setListener(dateTimePickerListener);
					fieldView = dateTimePickerField;
					break;
				case TIME_PICKER:
					// code level validation
					if (dateTimePickerListener == null) {
						throw new IllegalStateException(
						        "Field builder cannot create date pickers without a date time picker listener");
					}

					DateTimePickerField timePickerField = new DateTimePickerField(context,
					        formField, PickerType.TIME);
					timePickerField.setListener(dateTimePickerListener);
					fieldView = timePickerField;
					break;
				case BARCODE_SCANNER:
					BarcodeScannerField barcodeScannerField = new BarcodeScannerField(context,
					        formField);
					barcodeScannerField.setListener(barcodeScannerListener);
					fieldView = barcodeScannerField;
					break;
				case SINGLE_ATTACHMENT:
					String server = currentUser.getCompany().getServer();
					formField.setValue(server + formField.getValue());
					fieldView = new AttachmentField(context, formField);
					break;
				case DYNAMIC_IMAGE:
					DynamicImageField dynamicImageField = new DynamicImageField(context, formField);
					dynamicImageField
					        .setDynamicImageFieldActionListener(dynamicImageFieldActionListener);
					fieldView = dynamicImageField;
					break;
				default:
					fieldView = null;

			}
		} else {
			switch (formContentData.getType()) {
				case EMBEDDED_VIEW:
					EmbeddedViewData data = (EmbeddedViewData) formContentData;
					EmbeddedView embeddedView = new EmbeddedView(context, data);
					embeddedView.setEventsListener(embeddedViewEventsListener);

					fieldView = embeddedView;
					break;
				default:
					fieldView = null;
			}
		}

		if (fieldView != null) {
			// Set properties
			int visibility = formContentData.isHidden() ? View.GONE : View.VISIBLE;
			fieldView.setVisibility(visibility);
			fieldView.setEnabled(formContentData.isEnabled());
		}

		return fieldView;
	}

	// }}

	// =============================================================================
	// {{ Getters & Setters

	public List<View> getOrderedViewContents() {
		return documentViewContents;
	}

	// }}

}
